"""通用工具函数"""

import time
import hashlib
import hmac
import json
import random
import string
from typing import Dict, Any, List, Optional


def generate_nonce(length: int = 16) -> str:
    """
    生成随机字符串
    
    Args:
        length: 字符串长度
        
    Returns:
        随机字符串
    """
    return ''.join(random.choices(string.ascii_letters + string.digits, k=length))


def generate_timestamp() -> int:
    """
    生成当前时间戳
    
    Returns:
        时间戳（秒）
    """
    return int(time.time())


def generate_millisecond_timestamp() -> int:
    """
    生成当前毫秒级时间戳
    
    Returns:
        时间戳（毫秒）
    """
    return int(time.time() * 1000)


def md5(data: Union[str, bytes]) -> str:
    """
    计算MD5哈希值
    
    Args:
        data: 要哈希的数据
        
    Returns:
        MD5哈希值（十六进制字符串）
    """
    if isinstance(data, str):
        data = data.encode('utf-8')
    return hashlib.md5(data).hexdigest()


def sha1(data: Union[str, bytes]) -> str:
    """
    计算SHA1哈希值
    
    Args:
        data: 要哈希的数据
        
    Returns:
        SHA1哈希值（十六进制字符串）
    """
    if isinstance(data, str):
        data = data.encode('utf-8')
    return hashlib.sha1(data).hexdigest()


def sha256(data: Union[str, bytes]) -> str:
    """
    计算SHA256哈希值
    
    Args:
        data: 要哈希的数据
        
    Returns:
        SHA256哈希值（十六进制字符串）
    """
    if isinstance(data, str):
        data = data.encode('utf-8')
    return hashlib.sha256(data).hexdigest()


def hmac_sign(data: Union[str, bytes], 
              key: Union[str, bytes], 
              algorithm: str = 'sha256') -> str:
    """
    计算HMAC签名
    
    Args:
        data: 要签名的数据
        key: 密钥
        algorithm: 哈希算法，可选值：md5, sha1, sha256
        
    Returns:
        HMAC签名（十六进制字符串）
    """
    if isinstance(data, str):
        data = data.encode('utf-8')
    if isinstance(key, str):
        key = key.encode('utf-8')
    
    algorithms = {
        'md5': hashlib.md5,
        'sha1': hashlib.sha1,
        'sha256': hashlib.sha256
    }
    
    if algorithm not in algorithms:
        raise ValueError(f"不支持的算法: {algorithm}")
    
    h = hmac.new(key, data, algorithms[algorithm])
    return h.hexdigest()


def sign_dict(params: Dict[str, Any], 
              key: str, 
              algorithm: str = 'md5', 
              sort: bool = True, 
              exclude: List[str] = None) -> str:
    """
    对字典参数进行签名
    
    Args:
        params: 参数字典
        key: 密钥
        algorithm: 哈希算法
        sort: 是否按键名排序
        exclude: 排除的键名列表
        
    Returns:
        签名结果
    """
    exclude = exclude or []
    
    # 过滤参数
    filtered_params = {k: v for k, v in params.items() 
                     if k not in exclude and v is not None}
    
    # 按键名排序
    if sort:
        sorted_items = sorted(filtered_params.items())
    else:
        sorted_items = filtered_params.items()
    
    # 拼接字符串
    sign_str = '&'.join([f"{k}={v}" for k, v in sorted_items])
    
    # 添加密钥
    if key:
        sign_str += f"&key={key}" if algorithm == 'md5' else key
    
    # 计算签名
    if algorithm == 'md5':
        return md5(sign_str).upper()
    elif algorithm == 'sha1':
        return sha1(sign_str)
    elif algorithm == 'sha256':
        return sha256(sign_str)
    elif algorithm.startswith('hmac_'):
        hmac_alg = algorithm[5:]  # 移除 'hmac_' 前缀
        return hmac_sign(sign_str, key, hmac_alg)
    else:
        raise ValueError(f"不支持的算法: {algorithm}")


def validate_params(params: Dict[str, Any], 
                    required: List[str], 
                    optional: List[str] = None) -> bool:
    """
    验证参数
    
    Args:
        params: 参数字典
        required: 必填参数列表
        optional: 可选参数列表
        
    Returns:
        验证是否通过
        
    Raises:
        ValueError: 参数验证失败时抛出
    """
    # 检查必填参数
    for param in required:
        if param not in params or params[param] is None:
            raise ValueError(f"缺少必填参数: {param}")
    
    # 检查参数是否在允许的列表中
    if optional:
        all_params = required + optional
        for param in params:
            if param not in all_params:
                raise ValueError(f"未知参数: {param}")
    
    return True


def format_date(date_obj, format_str: str = '%Y-%m-%d %H:%M:%S') -> str:
    """
    格式化日期时间
    
    Args:
        date_obj: datetime对象或时间戳
        format_str: 格式字符串
        
    Returns:
        格式化后的日期时间字符串
    """
    import datetime
    
    if isinstance(date_obj, (int, float)):
        date_obj = datetime.datetime.fromtimestamp(date_obj)
    
    return date_obj.strftime(format_str)


def safe_json_dumps(data: Any, ensure_ascii: bool = False, **kwargs) -> str:
    """
    安全的JSON序列化函数
    
    Args:
        data: 要序列化的数据
        ensure_ascii: 是否确保ASCII编码
        **kwargs: 其他json.dumps参数
        
    Returns:
        JSON字符串
    """
    def default_handler(obj):
        """处理特殊类型"""
        import datetime
        
        if isinstance(obj, datetime.datetime):
            return format_date(obj)
        elif isinstance(obj, (datetime.date, datetime.time)):
            return str(obj)
        elif hasattr(obj, '__dict__'):
            return obj.__dict__
        else:
            return str(obj)
    
    return json.dumps(data, default=default_handler, ensure_ascii=ensure_ascii, **kwargs)


def safe_json_loads(json_str: str, **kwargs) -> Any:
    """
    安全的JSON反序列化函数
    
    Args:
        json_str: JSON字符串
        **kwargs: 其他json.loads参数
        
    Returns:
        反序列化后的数据
    """
    try:
        return json.loads(json_str, **kwargs)
    except (json.JSONDecodeError, TypeError):
        return json_str


def url_encode(params: Dict[str, Any], sort: bool = False) -> str:
    """
    URL编码参数字典
    
    Args:
        params: 参数字典
        sort: 是否按键名排序
        
    Returns:
        URL编码后的字符串
    """
    from urllib.parse import urlencode
    
    if sort:
        return urlencode(sorted(params.items()))
    return urlencode(params)


def convert_to_camel_case(snake_str: str) -> str:
    """
    将下划线命名转换为驼峰命名
    
    Args:
        snake_str: 下划线命名字符串
        
    Returns:
        驼峰命名字符串
    """
    components = snake_str.split('_')
    return components[0] + ''.join(x.title() for x in components[1:])


def convert_to_snake_case(camel_str: str) -> str:
    """
    将驼峰命名转换为下划线命名
    
    Args:
        camel_str: 驼峰命名字符串
        
    Returns:
        下划线命名字符串
    """
    import re
    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel_str)
    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()


def convert_dict_case(data: Dict[str, Any], 
                       converter: Callable[[str], str]) -> Dict[str, Any]:
    """
    转换字典键名的命名风格
    
    Args:
        data: 字典数据
        converter: 转换函数
        
    Returns:
        转换后的字典
    """
    result = {}
    for key, value in data.items():
        new_key = converter(key)
        if isinstance(value, dict):
            result[new_key] = convert_dict_case(value, converter)
        elif isinstance(value, list):
            result[new_key] = [
                convert_dict_case(item, converter) if isinstance(item, dict) else item
                for item in value
            ]
        else:
            result[new_key] = value
    return result


def dict_to_camel_case(data: Dict[str, Any]) -> Dict[str, Any]:
    """
    将字典键名从下划线命名转换为驼峰命名
    
    Args:
        data: 字典数据
        
    Returns:
        转换后的字典
    """
    return convert_dict_case(data, convert_to_camel_case)


def dict_to_snake_case(data: Dict[str, Any]) -> Dict[str, Any]:
    """
    将字典键名从驼峰命名转换为下划线命名
    
    Args:
        data: 字典数据
        
    Returns:
        转换后的字典
    """
    return convert_dict_case(data, convert_to_snake_case)


def parse_xml(xml_string: str) -> Dict[str, Any]:
    """
    解析XML字符串为字典
    
    Args:
        xml_string: XML字符串
        
    Returns:
        解析后的字典
    """
    import xml.etree.ElementTree as ET
    
    try:
        root = ET.fromstring(xml_string)
        return _element_to_dict(root)
    except ET.ParseError:
        raise ValueError("无效的XML格式")


def _element_to_dict(element) -> Dict[str, Any]:
    """
    递归将XML元素转换为字典
    """
    result = {}
    
    # 处理属性
    if element.attrib:
        result['@attributes'] = element.attrib
    
    # 处理子元素
    children = list(element)
    if children:
        child_dict = {}
        for child in children:
            child_data = _element_to_dict(child)
            if child.tag in child_dict:
                # 如果有多个相同标签的子元素，转换为列表
                if not isinstance(child_dict[child.tag], list):
                    child_dict[child.tag] = [child_dict[child.tag]]
                child_dict[child.tag].append(child_data)
            else:
                child_dict[child.tag] = child_data
        result.update(child_dict)
    
    # 处理文本内容
    if element.text and element.text.strip():
        if children or element.attrib:
            result['#text'] = element.text.strip()
        else:
            result = element.text.strip()
    
    return result


def format_size(size_bytes: int) -> str:
    """
    格式化文件大小
    
    Args:
        size_bytes: 字节大小
        
    Returns:
        格式化后的大小字符串
    """
    for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
        if size_bytes < 1024.0:
            return f"{size_bytes:.2f} {unit}"
        size_bytes /= 1024.0
    return f"{size_bytes:.2f} PB"


def is_valid_url(url: str) -> bool:
    """
    检查URL是否有效
    
    Args:
        url: URL字符串
        
    Returns:
        是否有效
    """
    import re
    url_pattern = re.compile(
        r'^(https?://)?'  # 协议（可选）
        r'(([0-9a-z_!~*\'().&=+$%-]+:)?[0-9a-z_!~*\'().&=+$%-]+@)?'  # 用户名密码（可选）
        r'(([0-9]{1,3}\.){3}[0-9]{1,3}'  # IP地址
        r'|'  # 或
        r'([0-9a-z_!~*\'()-]+\.)*'  # 域名前缀
        r'([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\.'  # 域名
        r'[a-z]{2,6})'  # 顶级域名
        r'(:[0-9]{1,5})?'  # 端口（可选）
        r'(/{1,})?'  # 路径分隔符
        r'([0-9a-z_!~*\'().;?:@&=+$,%#-]+/)*'  # 路径部分
        r'([0-9a-z_!~*\'().;?:@&=+$,%#-]+\.?'  # 文件名称
        r'[0-9a-z_!~*\'().;?:@&=+$,%#-]*'  # 扩展名
        r'(\?[0-9a-z_!~*\'().;?:@&=+$,%#-]*)?'  # 查询参数
        r'(#.*)?$',  # 锚点（可选）
        re.IGNORECASE
    )
    return bool(url_pattern.match(url))


def ensure_list(obj: Any) -> List[Any]:
    """
    确保对象是列表类型
    
    Args:
        obj: 任意对象
        
    Returns:
        列表
    """
    if obj is None:
        return []
    if isinstance(obj, list):
        return obj
    return [obj]


def flatten_dict(d: Dict[str, Any], parent_key: str = '', sep: str = '_') -> Dict[str, Any]:
    """
    扁平化嵌套字典
    
    Args:
        d: 嵌套字典
        parent_key: 父键名
        sep: 分隔符
        
    Returns:
        扁平化后的字典
    """
    items = []
    for k, v in d.items():
        new_key = f"{parent_key}{sep}{k}" if parent_key else k
        if isinstance(v, dict):
            items.extend(flatten_dict(v, new_key, sep=sep).items())
        else:
            items.append((new_key, v))
    return dict(items)